package mbp.agents;

import jade.core.Agent;
import jade.core.behaviours.OneShotBehaviour;
import jade.core.behaviours.TickerBehaviour;
import jade.core.behaviours.WakerBehaviour;
import jade.gui.AgentTree.AgentNode;
import jade.lang.acl.ACLMessage;
import jade.wrapper.AgentContainer;
import jade.wrapper.AgentController;
import jade.wrapper.StaleProxyException;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import mbp.agents.AMbpBaseAgent.AgentState;
import mbp.base.MessageType;
import mbp.base.Record;
import mbp.base.ResourceType;
import mbp.base.ServiceType;
import mbp.base.Settings;
import mbp.core.Intention;
import mbp.core.Offer;
import mbp.core.Resource;
import mbp.core.ResourceUtility;
import mbp.core.ResourceValidation;
import mbp.core.Service;
import mbp.core.ServiceTypeBuilder;
import mbp.exceptions.ParseExpressionErrorException;
import mbp.message.Copier;
import mbp.message.Message;
import mbp.message.ObjectBuilder;
import mbp.message.extra;
import mbp.message.resource;
import mbp.message.server;
import mbp.message.utility;

/**
 * Reprezentuje klienta pojedynczej us�ugi. Dla ka�dego typu us�ugi jest jeden agent zadania. 
 * Powo�uje on i nadzoruje klient�w us�ug, kt�rzy komunikuj� si� z serwerem us�ug
 * @author Wojciech Wilanski
 *
 */
public abstract class AMbpTaskAgent extends AMbpAgentList 
{
	// Inicjalizacja loggera
	//Logger logger = Logger.getLogger(this.getClass());
	
	// Stan agenta zadania
	public enum AgentState {
		NOSET, SET, COLLECTING, NEGOTIATION, RENEGOTIATION, ACCEPT, REALIZE, DONE
	}
	
	public enum Decision {
		ACCEPT, NEGOTIATE, REJECT
	}
	
	// Odpowiedz od agenta podstawowego
	protected enum AgentAnswer {
		NO, NOOFFER, OFFER, REJECT
	}
	
	// Lista odnalezionych serwerow oferujacych usluge
	protected List<server> serverList;
	
	// Lista ofert otrzymanych od agentow podstawowych <nazwa_agenta, oferta>
	protected Map<String, Offer> offerList;
	
	// Realizowana oferta
	protected Offer offer;
	
	private Map<String, AgentAnswer> baseAgentList;
	
	// Zasob do pozyskania
	protected ResourceValidation rcv;
	
	// Nazwa agenta procesu biznesowego
	private String rootAgent;
	
	private String packageName = Settings.packageName;
	
	private AMbpTaskAgent aMbpTaskAgent = this;
	
	protected AgentState agentState;
	
	Date dt = new Date();
	//Long nb = dt.getTime();
	Random generator = new Random(dt.getTime());
	
	
	@Override
	protected void setup()
	{
		super.setup();
		this.logger.setLevel(Level.ERROR);
		
		serverList = new ArrayList<server>();
		baseAgentList = new HashMap<String, AgentAnswer>();
		offerList = new HashMap<String, Offer>();
		
		// Pobranie parametrow z jakimi startuje agent:
		// 1 Nazwa agenta powolujacego (String)
		// 2 Zasob do pozyskania (ResourceValidation)
		// 3 Nazwa pakietu np: mbp.agents. Okresla lokalizacje klas agentow: 
		Object[] args = getArguments();
		
		
		if(args.length == 2 || args.length == 3)
		{
			rootAgent = (String) args[0];
			rcv = (ResourceValidation) args[1];
		}
		else
		{
			logger.error("Agent " + getAID().getName() + " error parameters count. Deleting agent...");
			this.doDelete();
		}
		
		if(args.length == 3)
		{
			packageName = (String) args[2];
		}
		
		logger.info("Agent " + getAID().getName() + " is ready.");
		logger.info("The name of the client agent: " + rootAgent);
		logger.info("Resource type to gain       : " + rcv.getType().getResourceTypeName());
		
		//TaskOnStart taskOnStart = new TaskOnStart();
		//addBehaviour(taskOnStart);
		
		addBehaviour(new WakerBehaviour(this, 300) {
			protected void handleElapsedTimeout(){
				agentState = AgentState.NOSET;
				processNegotiation('C', null);
			}
		});
		
		//Zapytanie agenta rejstru o nazwy producentow
		//discoverServers(rcv.getType().getResourceTypeName());
		
//		addBehaviour(new WakerBehaviour(this, 5000) {
//			protected void handleElapsedTimeout() {
//				CollectOffers collectOffers = new CollectOffers(aMbpTaskAgent, 2000);
//				aMbpTaskAgent.addBehaviour(collectOffers);
//			}
//		});	
	}
	
//	public class TaskOnStart extends OneShotBehaviour
//	{
//
//		@Override
//		public void action() {
//			agentState = AgentState.NOSET;
//			processNegotiation('C', null);
//		}
//		
//	}
	
	/**
	 * Wysyla zapytanie do rejestru uslug o udostepnienie listy serwerow swiadczacych usluge.
	 * Zapytanie o usluge ktorej wynikiem jest produkcja zasobu o podanym typie.
	 * @param rcType
	 */
	public void discoverServers(String rcType)
	{
		// Typ wiadomosci
		Message msg = new Message(MessageType.SERVERLGET, getLocalName(), Settings.registerName);
		
		// Specyfikacja zapytania
		extra ext = new extra();
		ext.name = "resource";
		ext.value = rcType;
		List<extra> extList = new ArrayList<extra>();
		extList.add(ext);
		msg.getContentXml().extras = extList;
		
		// Wyslanie komunikatu
		SenderLogger.send(this, msg.GetACLMessage());
	}

	@Override
	public void processMessage(Message iMsg, String senderName) 
	{
		MessageType messageType = iMsg.getMessageType();
		switch(messageType)
		{
		// Odebranie oferty od agenta podstawowego
		case OFFER:
			onOffer(iMsg, senderName);
			break;
		case COUNTEROFFER:
			onCounterOffer(iMsg, senderName);
			break;
		case NOOFFER:
			onNoOffer(iMsg, senderName);
			break;
		// Odebranie od Agenta Procesu Biznesowego zgody na podpisanie kontraktu
		case ACCEPTOFFER:
			onAcceptOffer(iMsg, senderName);
			break;
		case ACCEPTCONTRACT:
			onAcceptContract(iMsg, senderName);
			break;
		case CONTRACT:
			onContract(iMsg, senderName);
			break;
		// Odebranie listy serwerow
		case SERVERL:
			onServerL(iMsg, senderName);
			break;
		// Odebranie listy zasobow
		case RESOURCEL:
			onResourceL(iMsg, senderName);
			break;
		// Odebranie listy funkcji uzytecznosci
		case UTILITYL:
			onUtilityL(iMsg, senderName);
			break;
		// Odebranie zadania zakonczenia dzialania agenta
		case DODELETE:
			onDoDelete(iMsg, senderName);
			break;
		case REJECT:
			onReject(iMsg, senderName);
			break;
		case FREEZE:
			onFeeze(iMsg, senderName);
			break;
		case RESUME:
			onResume(iMsg, senderName);
			break;
		case DONE:
			onDone(iMsg, senderName);
			break;
		default:
			logger.warn("Message type: " + messageType + " not supported");
			break;			
		}
		// TODO Auto-generated method stub
		
	}
	
	/**
	 * Odebranie listy serwerow od agenta (rejestru)
	 * @param iMsg
	 * @param senderName
	 */
	private void onServerL(mbp.message.Message iMsg, String senderName)
	{
		serverList = iMsg.getContentXml().servers;
		
		// W przypadku gdy nie odnaleziono zadnych serwerow
		if(serverList.size() == 0)
		{
			sendNoService();
			return;
		}
		
		for(int i = 0; i<serverList.size(); i++)
		{
			logger.debug("Recieved server info - name: " + serverList.get(i).name + ", type: " + serverList.get(i).type);
		}
		requestForObjects();
		agentState = AgentState.SET;
		
		startBaseAgents();
		agentState = AgentState.COLLECTING;
		
		//collectOffers();
	}
	
	
//	private void collectOffers()
//	{
//		addBehaviour(new WakerBehaviour(this, 5000) {
//			protected void handleElapsedTimeout() {
//				CollectOffers collectOffers = new CollectOffers(aMbpTaskAgent, 2000);
//				aMbpTaskAgent.addBehaviour(collectOffers);
//			}
//		});	
//	}
	
	/**
	 * Sprawdza czy nadeszly wszystkie odpowiedzi od agentow podstawowych.
	 * Odpowiedzia moze byc oferta lub rezygnacja
	 * @return
	 */
	private boolean checkAnswres()
	{
		boolean result = true;
		Iterator<String> it = baseAgentList.keySet().iterator();
		
		while(it.hasNext())
		{
			String key = it.next();
			if(baseAgentList.get(key) == AgentAnswer.NO)
			{
				result = false;
				break;
			}
		}
		
		if(result)
		{
			Iterator<String> it2 = baseAgentList.keySet().iterator();
			while(it2.hasNext())
			{
				baseAgentList.put(it2.next(), AgentAnswer.NO);
			}
		}
		
		return result;
	}
	
	/**
	 * Obsluga wiadomosci RESOURCEL. Odebranie listy dostepnych zasobow od agenta procesu biznesowgo
	 * @param iMsg
	 * @param senderName
	 */
	private void onResourceL(mbp.message.Message iMsg, String senderName)
	{
		resourceList.clear();
		List<resource> rciList = iMsg.getContentXml().resources;
		ObjectBuilder objectBuilder = new ObjectBuilder();
		for(int i = 0; i<rciList.size(); i++)
		{
			//resourceList.put(new String(rciList.get(i).resourceid), objectBuilder.getResource(rciList.get(i)));
			resourceList.add(new Record<Resource, Object[]>( objectBuilder.getResource(rciList.get(i)), null));
		}
	}
	
	/**
	 * Obsluga wiadomosci UTILITYL. Odebranie listy funkcji uzytecznosci od agenta procesu biznesowgo
	 * @param iMsg
	 * @param senderName
	 */
	private void onUtilityL(mbp.message.Message iMsg, String senderName)
	{
		List<utility> rcuiList = iMsg.getContentXml().utilities;
		ObjectBuilder objectBuilder = new ObjectBuilder();
		for(int i = 0; i<rcuiList.size(); i++)
		{
			//utilityList.put(new String(rcuiList.get(i).type), objectBuilder.getResourceUtility(rcuiList.get(i)));
			utilityList.add(new Record<ResourceUtility, Object[]>(objectBuilder.getResourceUtility(rcuiList.get(i)), null));
		}
	}
	
	/**
	 * Obsluga zerwana kontraktu, nagocjacji
	 * @param iMsg
	 * @param senderName
	 */
	private void onReject(mbp.message.Message iMsg, String senderName)
	{
		// Nadawca jest agent procesu biznesowego, przekazanie informacji
		if(senderName.compareTo(rootAgent)==0)
		{
			if(agentState == AgentState.REALIZE)
			{
				if(baseAgentList.size() == 1)
				{
					String baseAgent = baseAgentList.keySet().iterator().next();
					Message iMsgAnswer = new Message(MessageType.REJECT, this.getLocalName(), baseAgent);
					iMsgAnswer.getContentXml().extras = iMsg.getContentXml().extras;
					SenderLogger.send(this, iMsgAnswer.GetACLMessage());
					//agentState = AgentState.NOSET;
				}
			}
		}
		else
		{
			// Zbieranie odpowiedzi od agentow podstawowych
			if(agentState == AgentState.COLLECTING)
			{
				if(baseAgentList.containsKey(senderName))
				{
					baseAgentList.put(senderName, AgentAnswer.REJECT);
				}
				
				printBaseAgentsAnswers();
				if(checkAnswres())
				{
					logger.debug("Collected all answers from base agents. Ccangong state to NEGOTIATION");
					agentState = AgentState.NEGOTIATION;
					processNegotiation('C', null);
				}
			}
		
			if(agentState == AgentState.REALIZE)
			{
				Message iMsgAnswer = new Message(MessageType.REJECT, this.getLocalName(), rootAgent);
				iMsgAnswer.getContentXml().extras = iMsg.getContentXml().extras;
				iMsgAnswer.getContentXml().resources = iMsg.getContentXml().resources;
				SenderLogger.send(this, iMsgAnswer.GetACLMessage());
				//agentState = AgentState.NOSET;
			}
		}
		
	}
	
	/**
	 * Zakonczenie kontraktu
	 * @param iMsg
	 * @param senderName
	 */
	private void onDone(mbp.message.Message iMsg, String senderName)
	{
		if(agentState == AgentState.REALIZE)
		{
			Message iMsgAnswer = new Message(MessageType.DONE, this.getLocalName(), rootAgent);
			iMsgAnswer.getContentXml().contracts = iMsg.getContentXml().contracts;
			iMsgAnswer.getContentXml().resources = iMsg.getContentXml().resources;
			SenderLogger.send(this, iMsgAnswer.GetACLMessage());
			agentState = AgentState.DONE;
		}
	}
	
	private void onNoOffer(mbp.message.Message iMsg, String senderName)
	{
		// Zbieranie odpowiedzi od agentow podstawowych
		if(agentState == AgentState.COLLECTING)
		{
			if(baseAgentList.containsKey(senderName))
			{
				baseAgentList.put(senderName, AgentAnswer.NOOFFER);
			}
			
			printBaseAgentsAnswers();
			if(checkAnswres())
			{
				logger.debug("Collected all answers from base agents. Ccangong state to NEGOTIATION");
				agentState = AgentState.NEGOTIATION;
				processNegotiation('C', null);
			}
		}
	}
	
	/**
	 * Zapisanie oferty od agenta podstawowego
	 * @param iMsg
	 * @param senderName
	 */
	private void onOffer(mbp.message.Message iMsg, String senderName)
	{
		// Zbieranie odpowiedzi od agentow podstawowych
		if(agentState == AgentState.COLLECTING)
		{
			if(baseAgentList.containsKey(senderName))
			{
				Offer offer = iMsg.getOffer();
				baseAgentList.put(senderName, AgentAnswer.OFFER);
				offerList.put(senderName, offer);
			}
			
			printBaseAgentsAnswers();
			if(checkAnswres())
			{
				logger.debug("Collected all answers from base agents. Ccangong state to NEGOTIATION");
				agentState = AgentState.NEGOTIATION;
				processNegotiation('C', null);
			}
		}
		
	}
	
	private void onCounterOffer(mbp.message.Message iMsg, String senderName)
	{
		
	}
	
	private void onAcceptOffer(mbp.message.Message iMsg, String senderName)
	{
		// Jezeli nadawca nie jest jego agent procesu biznesowego
		if(senderName.compareTo(rootAgent)!=0)
		{
			Message iMsgAnswer = new Message(MessageType.ERROR, this.getLocalName(), senderName);
			extra exErr = new extra();
			exErr.name = "msg";
			exErr.value = "Agent Identification ERROR";
			iMsgAnswer.getContentXml().extras.add(exErr);
			SenderLogger.send(this, iMsg.GetACLMessage());
			return;
		}
		
		// Jezeli agent nie jest w stanie negocjacji
		if(agentState != AgentState.NEGOTIATION)
		{
			Message iMsgAnswer = new Message(MessageType.ERROR, this.getLocalName(), senderName);
			extra exErr = new extra();
			exErr.name = "msg";
			exErr.value = "Agent State ERROR. Agent state is: " + this.agentState + ". TheAgent state should be: NEGOTIATION";
			iMsgAnswer.getContentXml().extras.add(exErr);
			SenderLogger.send(this, iMsg.GetACLMessage());
			return;
		}
		
		extra ex = iMsg.getContentXml().extras.get(0);
		String uuid = ex.value;
		String baseAgentName = null;
		
		// Odszukanie oferty
		Iterator<String> it = offerList.keySet().iterator();
		while(it.hasNext())
		{
			String key = it.next();
			if(offerList.get(key).GetUUID().compareTo(uuid)==0)
			{
				baseAgentName = new String(key);
				break;
			}
		}
		
		// Przekazanie akceptacji do agenta podstawowego
		if(baseAgentName != null)
		{
			//offer = offerList.get(uuid);
			//List<resource> rcInAdvance = iMsg.getContentXml().resources;
			
			Message iMsgAnswer = new Message(MessageType.ACCEPTOFFER, this.getLocalName(), baseAgentName);
			iMsgAnswer.getContentXml().extras.add(ex);
			//iMsgAnswer.getContentXml().resources = rcInAdvance;
			SenderLogger.send(this, iMsgAnswer.GetACLMessage());
			this.agentState = AgentState.ACCEPT;
		}
	}
	
	/**
	 * Potwierdzenie podpisania kontraktu
	 * @param iMsg
	 * @param senderName
	 */
	private void onAcceptContract(mbp.message.Message iMsg, String senderName)
	{
		if(agentState == AgentState.ACCEPT)
		{
			String uuid = new String( iMsg.getContentXml().contracts.get(0).uuid );
			
			Message iMsgAnswer = new Message(MessageType.ACCEPTCONTRACT, this.getLocalName(), rootAgent );
			iMsgAnswer.getContentXml().contracts = iMsg.getContentXml().contracts;
			SenderLogger.send(this, iMsgAnswer.GetACLMessage());
			this.agentState = AgentState.REALIZE;
			
			// Usuniecie pozostalych agentow podstawowych
			Iterator<String> it = baseAgentList.keySet().iterator();
			while(it.hasNext())
			{
				String key = it.next();
				if(key.compareTo(senderName)!=0)
				{
					deleteBaseAgent(key);
					it.remove();
				}
			}
			
			// Zapamietanie realizowanej oferty
			offer = offerList.remove(uuid);
			
			// Usuniecie pozostalych ofert
			offerList.clear();
			
		}
	}
	
	/**
	 * Odebranie informacji o kontrakcie od agenta zadania i przekazanie do agenta procesu biznesowegp
	 * @param iMsg
	 * @param senderName
	 */
	private void onContract(mbp.message.Message iMsg, String senderName)
	{
		if(agentState == AgentState.REALIZE)
		{
			Message iMsgAnswer = new Message(MessageType.CONTRACT, this.getLocalName(), rootAgent );
			iMsgAnswer.getContentXml().contracts = iMsg.getContentXml().contracts;
			SenderLogger.send(this, iMsgAnswer.GetACLMessage());
		}
	}
	
	/**
	 * Przekazanie wiadomosci o wstrzymaniu kontraktu
	 * @param iMsg
	 * @param senderName
	 */
	private void onFeeze(mbp.message.Message iMsg, String senderName)
	{
		if(agentState == AgentState.REALIZE)
		{
			if(baseAgentList.size() == 1)
			{
				String baseAgent = baseAgentList.keySet().iterator().next();
				Message iMsgAnswer = new Message(MessageType.FREEZE, this.getLocalName(), baseAgent );
				iMsgAnswer.getContentXml().extras = iMsg.getContentXml().extras;
				SenderLogger.send(this, iMsgAnswer.GetACLMessage());
			}
		}
	}
	
	private void onResume(mbp.message.Message iMsg, String senderName)
	{
		if(agentState == AgentState.REALIZE)
		{
			if(baseAgentList.size() == 1)
			{
				String baseAgent = baseAgentList.keySet().iterator().next();
				Message iMsgAnswer = new Message(MessageType.RESUME, this.getLocalName(), baseAgent );
				iMsgAnswer.getContentXml().extras = iMsg.getContentXml().extras;
				SenderLogger.send(this, iMsgAnswer.GetACLMessage());
			}
		}
	}
	
	/**
	 * Obsluga zadania: Zakonczenie pracy agenta
	 * @param iMsg
	 * @param senderName
	 */
	private void onDoDelete(mbp.message.Message iMsg, String senderName)
	{
		if(senderName.compareTo(rootAgent)==0)
		{
			logger.info("Deleting agent ...");
			deleteBaseAgents();
			
			addBehaviour(new WakerBehaviour(this, 500) {
				protected void handleElapsedTimeout(){
					doDelete();
				}
			});
		}
		else
		{
			logger.warn("Ignoring DODELETE message. Only " + rootAgent + " is permited to delete me.");
		}
	}
	
	/**
	 * Przygotowuje i wysyla zadanie przeslania obiektow Resource i ResourceUtility
	 * do agenta procesu biznesowego. Zapytanie jest przygotowywane po konkretne obiekty
	 * wyznaczone na podstawie typu uslugi oferowanej przez serwer.
	 */
	private void requestForObjects()
	{
		// Typ uslugi, pobierany jest od pierwszego serwera z listy
		String serviceType = serverList.get(0).type;
		
		// Lista zasobow wejsciowych
		ServiceTypeBuilder stb = new ServiceTypeBuilder("");
		ServiceType srv = stb.GetServiceType(serviceType);
		List<ResourceType> rcTypes = srv.getResourceTypesIn();
		
		// Lista typow dla obiektow Resource (zasob)
		// List<extra> rcTypeNames = new ArrayList<extra>();
		// Lista typow dla obiektow ResourceUtility (funkcja uzytecznosci)
		List<extra> rcuTypeNames = new ArrayList<extra>();
		
		extra ex = new extra();
		ex.name = "type";
		ex.value = rcv.getType().getResourceTypeName();
		rcuTypeNames.add(ex);
		
		for(int i = 0; i<rcTypes.size(); i++)
		{
			//extra ex1 = new extra();
			extra ex2 = new extra();
			//ex1.name = "type";
			//ex1.value = rcTypes.get(i).getResourceTypeName();
			ex2.name = "type";
			ex2.value = rcTypes.get(i).getResourceTypeName();
			//rcTypeNames.add(ex1);
			rcuTypeNames.add(ex2);
		}
		
		// Przygotowanie i wyslanie zadania listy dostepnych zasobow
		// Message message1 = new Message(MessageType.RESOURCELGET, getLocalName(), rootAgent);
		// message1.getContentXml().extras = rcTypeNames;
		// ACLMessage msgReply1 = message1.GetACLMessage();
		// SenderLogger.send(this, msgReply1);
		
		// Przygotowanie i wyslanie zadania funkcji uzytecznosci
		Message message2 = new Message(MessageType.UTILITYLGET, getLocalName(), rootAgent);
		message2.getContentXml().extras = rcuTypeNames;
		ACLMessage msgReply2 = message2.GetACLMessage();
		SenderLogger.send(this, msgReply2);
		
	}
	
	/**
	 * Przygotowuje i uruchamia Agentow Podstawowych
	 */
	public void startBaseAgents()
	{
		Intention intention = prepareIntention();
		
		for(int i = 0; i<serverList.size(); i++)
		{
			// Utworzenie unikatowej nazwy dla agenta zadania
			//StringBuilder sb = new StringBuilder();
			//sb.append(serverList.get(i).name);
			//sb.append(generator.nextInt());
			//String atName = sb.toString();
			String atName = java.util.UUID.randomUUID().toString();
			logger.debug("BaseAgent name to start: " + atName);
			
			Intention intn = Copier.cloneIntention(intention);
			String serverName = new String(serverList.get(i).name);
			
			if(Settings.isSniffer && Settings.sniffBase) {
				addAgentToSniffer(atName);
			}
			
			if(startAgent(packageName + ".MbpBaseAgent", atName, new Object[]{getLocalName(), serverName, intn}))
			{
				baseAgentList.put(atName, AgentAnswer.NO);
			}
		}
	}
	
	
	/**
	 * Uruchomienie agenta. Metoda wykorzystywana do uruchamiania agentow podstawowych
	 * @param agentClass
	 * @param agentName
	 * @param params
	 * @return
	 */
	public boolean startAgent(String agentClass, String agentName, Object[] params)
	{
		boolean result;
		AgentContainer ac = getContainerController();
		try 
		{
			AgentController agntToStart = ac.createNewAgent(agentName, agentClass, params);
			agntToStart.start();
			result = true;
		}
		catch (StaleProxyException ex1) 
		{
			logger.error("StaleProxyException", ex1);
			result = false;
		}
		catch (Exception ex2)
		{
			logger.error("Exception", ex2);
			result = false;
		}
		
		return result;
	}
	

	
	/**
	 * Usuniecie wszystkich powojanych agentow podstawowych
	 */
	private void deleteBaseAgents()
	{
		Iterator<String> it = baseAgentList.keySet().iterator();
		while(it.hasNext())
		{
			String key = it.next();
			Message message = new Message(MessageType.DODELETE, getLocalName(), key);
			ACLMessage msgReply = message.GetACLMessage();
			SenderLogger.send(this, msgReply);	
		}
	}
	
	private void deleteBaseAgent(String agentName)
	{
		//if(baseAgentList.containsKey(agentName))
		//{
			Message message = new Message(MessageType.DODELETE, getLocalName(), agentName);
			ACLMessage msgReply = message.GetACLMessage();
			SenderLogger.send(this, msgReply);
			//baseAgentList.remove(agentName);
		//}
	}
	
	
	
	private void processNegotiation(Character sender, MessageType messageType)
	{
		switch(sender)
		{
		case 'C':
			if(agentState == AgentState.NOSET)
			{
				discoverServers( rcv.getType().getResourceTypeName() );
			}
			
			if(agentState == AgentState.NEGOTIATION)
			{
				Decision decision = decision();
				logger.debug("Decision: " + decision);
				switch(decision)
				{
				case ACCEPT:
					// Wybranie najlepszej oferty
					Offer offer = selectBestOffer();
					if(checkChanceToSign(offer))
					{
						// Wyslanie checi podpisania kontraktu do serwera
					}
					else
					{
						// Przekazanie wynegocjowanej oferty do agenta procesu biznesowego
						sendOffer(offer);
					}
					break;
				case NEGOTIATE:
					break;
				case REJECT:
					if(offerList.size() == 0)
					{
						sendNoOffer();
					}
					else
					{
						sendResignation(null);
					}
					break;
				}
			}
			break;
		case 'S':
			
			break;
		}
	}
	
	/**
	 * Wysyla oferte do swojego Agenta Procesu Biznesowego
	 * @param offer
	 */
	public void sendOffer(Offer offer)
	{
		Message iMsg = new Message(MessageType.OFFER, this.getLocalName(), rootAgent);
		iMsg.setOffer(offer);
		SenderLogger.send(this, iMsg.GetACLMessage());
		logger.info("Message type: " + MessageType.OFFER + " has been sent to: " + rootAgent);
	}
	
	public void sendNoService()
	{
		logger.debug("No servers recieved !!!");
		Message message = new Message(MessageType.NOSERVICE, getLocalName(), rootAgent);
		ACLMessage msgReply = message.GetACLMessage();
		SenderLogger.send(this, msgReply);
	}
	
	private void sendResignation(String uuid)
	{
		if(uuid == null)uuid = "0";
		Message iMsg = new Message(MessageType.REJECT, this.getLocalName(), rootAgent);
		extra ex = new extra();
		ex.name = "uuid";
		ex.value = uuid;
		iMsg.getContentXml().extras.add(ex);
		SenderLogger.send(this, iMsg.GetACLMessage());
	}
	
	private void sendNoOffer()
	{
		Message iMsg = new Message(MessageType.NOOFFER, this.getLocalName(), rootAgent);
		SenderLogger.send(this, iMsg.GetACLMessage());
	}
	
	/**
	 * Sprawdza brakujace zasoby w odniesieniu do przekazanej oferty
	 * @param offer
	 * @return
	 */
	private List<ResourceValidation> checkResources(Offer offer)
	{
		// Lista na brakujace zasoby
		List<ResourceValidation> missingRc = new ArrayList<ResourceValidation>();
		
		// Lista wszystkich zasobow wejsciowych uslugi
		List<ResourceValidation> rcvForOffer = offer.getResourcesIn(-1);
		
		
		boolean isOnList;
		
		// Przjscie przez wszystkie zasoby wejsciowe uslugi
		for(int i = 0; i<rcvForOffer.size(); i++)
		{
			isOnList = false;
			ResourceValidation rcvFO = rcvForOffer.get(i);
			String rcvType = rcvFO.getType().getResourceTypeName();
			
			for(int j = 0; j<resourceList.size(); j++)
			{
				Resource rc = resourceList.get(j).value0;
				String rcType = rc.getType().getResourceTypeName();
				
				// Inny typ zasobu
				if(rcType.compareTo(rcvType) != 0)
					continue;
				
				try {
					if( rcvFO.validateCondition(rc) )
					{
						isOnList = true;
						break;
					}
				} catch (ParseExpressionErrorException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}		
			}
			
						
			// Jesli zasobu nie odnaleziono na liscie zasobow dostepnych, kopia obiektu ResourceValidation
			// z listy zasobow wejsciowych dodawana jest do listy brakujacych
			if(!isOnList)
			{
				missingRc.add( Copier.cloneResourceValidation(rcvForOffer.get(i)) );
			}
		}		
		return missingRc;
	}
	
	/**
	 * Sprawdza czy jest mozliwosc podpisania kontraktu w oparciu o informacje, o dostepnych
	 * zasobach ktore posiada agent od agenta procesu biznesowego.
	 * @param offer
	 * @return
	 */
	private boolean checkChanceToSign(Offer offer)
	{
		return false;
	}
	
	/**
	 * Przygotowanie intencji dla agentow podstawowych
	 * @return
	 */
	public abstract Intention prepareIntention();
	
	/**
	 * Przygotowanie kontroferty dla agentow podstawowych
	 * @return
	 */
	public abstract Intention prepareCounterOffer();
	
	/**
	 * Decyzja po skompletowaniu ofert.
	 * @return
	 *  ACCEPT - Akceptacja, wybor nastapi sposrod listy odebranych ofert.<br>
	 *  NEGOTIATE - Negocjowac dalej. Przygotowac i wyslac kontroferty.<br>
	 *  REJECT - Odrzucenie wszystkich ofert
	 */
	public abstract Decision decision();
	
	/**
	 * Wybiera najlepsza oferte z listy ofert nadeslanych przez agentow podstawowych.<br>
	 * Lista ofert: offerList
	 * @return
	 * Offer - wybrana oferta
	 */
	public abstract Offer selectBestOffer();

	
	
	//-------------------------------------------------------------------------------------//
	// Klasy wewnetrzne                                                                    //
	//-------------------------------------------------------------------------------------//
	
//	public class CollectOffersStarter extends OneShotBehaviour
//	{
//
//		@Override
//		public void action() {
//			// TODO Auto-generated method stub
//			
//		}
//		
//	}
//	
//	public class CollectOffers extends TickerBehaviour
//	{
//
//		public CollectOffers(Agent a, long period) {
//			super(a, period);
//			// TODO Auto-generated constructor stub
//		}
//
//		@Override
//		protected void onTick() 
//		{
//			// TODO Auto-generated method stub
//			//logger.info("Collect offers..........................");
//			Iterator<String> it = baseAgentList.keySet().iterator();
//			int counter = 0;
//			boolean collected = true;
//			while(it.hasNext())
//			{
//				String key = it.next();
//				if(baseAgentList.get(key)==null)
//				{
//					collected = false;
//				}
//			}
//				
//			if(collected)
//			{
//				logger.info("Collected all offers from base agents");
//				this.stop();
//			}
//			else
//			{
//				logger.info("Waiting for all offers ...");
//			}
//			
//			if(this.getTickCount() == 10)
//			{
//				logger.info("Finished waiting for offers.");
//				this.stop();
//			}
//		}
//		
//	}
	
	//-------------------------------------------------------------------------------------//
	// Metody testowe                                                                      //
	//-------------------------------------------------------------------------------------//
	
	public void printBaseAgentsAnswers()
	{
		StringBuilder sb = new StringBuilder();
		Iterator<String> it = baseAgentList.keySet().iterator();
		while(it.hasNext())
		{
			String key = it.next();
			sb.append(" Agent: " + key + ", Answer: " + baseAgentList.get(key) + "\n");
		}
		
		logger.info("Base Agents Answers \n" + sb.toString());
					
	}
	

}
